/*-------------------------------------------------------------------------
 *
 * spgxlog.h
 *      xlog declarations for SP-GiST access method.
 *
 * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 * src/include/access/spgxlog.h
 *
 *-------------------------------------------------------------------------
 */
#ifndef SPGXLOG_H
#define SPGXLOG_H

#include "access/xlogreader.h"
#include "lib/stringinfo.h"
#include "storage/off.h"

/* XLOG record types for SPGiST */
#define XLOG_SPGIST_CREATE_INDEX    0x00
#define XLOG_SPGIST_ADD_LEAF        0x10
#define XLOG_SPGIST_MOVE_LEAFS        0x20
#define XLOG_SPGIST_ADD_NODE        0x30
#define XLOG_SPGIST_SPLIT_TUPLE        0x40
#define XLOG_SPGIST_PICKSPLIT        0x50
#define XLOG_SPGIST_VACUUM_LEAF        0x60
#define XLOG_SPGIST_VACUUM_ROOT        0x70
#define XLOG_SPGIST_VACUUM_REDIRECT 0x80

/*
 * Some redo functions need an SpGistState, although only a few of its fields
 * need to be valid.  spgxlogState carries the required info in xlog records.
 * (See fillFakeState in spgxlog.c for more comments.)
 */
typedef struct spgxlogState
{
    TransactionId myXid;
    bool        isBuild;
} spgxlogState;

/*
 * Backup Blk 0: destination page for leaf tuple
 * Backup Blk 1: parent page (if any)
 */
typedef struct spgxlogAddLeaf
{
    bool        newPage;        /* init dest page? */
    bool        storesNulls;    /* page is in the nulls tree? */
    OffsetNumber offnumLeaf;    /* offset where leaf tuple gets placed */
    OffsetNumber offnumHeadLeaf;    /* offset of head tuple in chain, if any */

    OffsetNumber offnumParent;    /* where the parent downlink is, if any */
    uint16        nodeI;

    /* new leaf tuple follows (unaligned!) */
} spgxlogAddLeaf;

/*
 * Backup Blk 0: source leaf page
 * Backup Blk 1: destination leaf page
 * Backup Blk 2: parent page
 */
typedef struct spgxlogMoveLeafs
{
    uint16        nMoves;            /* number of tuples moved from source page */
    bool        newPage;        /* init dest page? */
    bool        replaceDead;    /* are we replacing a DEAD source tuple? */
    bool        storesNulls;    /* pages are in the nulls tree? */

    /* where the parent downlink is */
    OffsetNumber offnumParent;
    uint16        nodeI;

    spgxlogState stateSrc;

    /*----------
     * data follows:
     *        array of deleted tuple numbers, length nMoves
     *        array of inserted tuple numbers, length nMoves + 1 or 1
     *        list of leaf tuples, length nMoves + 1 or 1 (unaligned!)
     *
     * Note: if replaceDead is true then there is only one inserted tuple
     * number and only one leaf tuple in the data, because we are not copying
     * the dead tuple from the source
     *----------
     */
    OffsetNumber offsets[FLEXIBLE_ARRAY_MEMBER];
} spgxlogMoveLeafs;

#define SizeOfSpgxlogMoveLeafs    offsetof(spgxlogMoveLeafs, offsets)

/*
 * Backup Blk 0: original page
 * Backup Blk 1: where new tuple goes, if not same place
 * Backup Blk 2: where parent downlink is, if updated and different from
 *                 the old and new
 */
typedef struct spgxlogAddNode
{
    /*
     * Offset of the original inner tuple, in the original page (on backup
     * block 0).
     */
    OffsetNumber offnum;

    /*
     * Offset of the new tuple, on the new page (on backup block 1). Invalid,
     * if we overwrote the old tuple in the original page).
     */
    OffsetNumber offnumNew;
    bool        newPage;        /* init new page? */

    /*----
     * Where is the parent downlink? parentBlk indicates which page it's on,
     * and offnumParent is the offset within the page. The possible values for
     * parentBlk are:
     *
     * 0: parent == original page
     * 1: parent == new page
     * 2: parent == different page (blk ref 2)
     * -1: parent not updated
     *----
     */
    int8        parentBlk;
    OffsetNumber offnumParent;    /* offset within the parent page */

    uint16        nodeI;

    spgxlogState stateSrc;

    /*
     * updated inner tuple follows (unaligned!)
     */
} spgxlogAddNode;

/*
 * Backup Blk 0: where the prefix tuple goes
 * Backup Blk 1: where the postfix tuple goes (if different page)
 */
typedef struct spgxlogSplitTuple
{
    /* where the prefix tuple goes */
    OffsetNumber offnumPrefix;

    /* where the postfix tuple goes */
    OffsetNumber offnumPostfix;
    bool        newPage;        /* need to init that page? */
    bool        postfixBlkSame; /* was postfix tuple put on same page as
                                 * prefix? */

    /*
     * new prefix inner tuple follows, then new postfix inner tuple (both are
     * unaligned!)
     */
} spgxlogSplitTuple;

/*
 * Buffer references in the rdata array are:
 * Backup Blk 0: Src page (only if not root)
 * Backup Blk 1: Dest page (if used)
 * Backup Blk 2: Inner page
 * Backup Blk 3: Parent page (if any, and different from Inner)
 */
typedef struct spgxlogPickSplit
{
    bool        isRootSplit;

    uint16        nDelete;        /* n to delete from Src */
    uint16        nInsert;        /* n to insert on Src and/or Dest */
    bool        initSrc;        /* re-init the Src page? */
    bool        initDest;        /* re-init the Dest page? */

    /* where to put new inner tuple */
    OffsetNumber offnumInner;
    bool        initInner;        /* re-init the Inner page? */

    bool        storesNulls;    /* pages are in the nulls tree? */

    /* where the parent downlink is, if any */
    bool        innerIsParent;    /* is parent the same as inner page? */
    OffsetNumber offnumParent;
    uint16        nodeI;

    spgxlogState stateSrc;

    /*----------
     * data follows:
     *        array of deleted tuple numbers, length nDelete
     *        array of inserted tuple numbers, length nInsert
     *        array of page selector bytes for inserted tuples, length nInsert
     *        new inner tuple (unaligned!)
     *        list of leaf tuples, length nInsert (unaligned!)
     *----------
     */
    OffsetNumber offsets[FLEXIBLE_ARRAY_MEMBER];
} spgxlogPickSplit;

#define SizeOfSpgxlogPickSplit offsetof(spgxlogPickSplit, offsets)

typedef struct spgxlogVacuumLeaf
{
    uint16        nDead;            /* number of tuples to become DEAD */
    uint16        nPlaceholder;    /* number of tuples to become PLACEHOLDER */
    uint16        nMove;            /* number of tuples to move */
    uint16        nChain;            /* number of tuples to re-chain */

    spgxlogState stateSrc;

    /*----------
     * data follows:
     *        tuple numbers to become DEAD
     *        tuple numbers to become PLACEHOLDER
     *        tuple numbers to move from (and replace with PLACEHOLDER)
     *        tuple numbers to move to (replacing what is there)
     *        tuple numbers to update nextOffset links of
     *        tuple numbers to insert in nextOffset links
     *----------
     */
    OffsetNumber offsets[FLEXIBLE_ARRAY_MEMBER];
} spgxlogVacuumLeaf;

#define SizeOfSpgxlogVacuumLeaf offsetof(spgxlogVacuumLeaf, offsets)

typedef struct spgxlogVacuumRoot
{
    /* vacuum a root page when it is also a leaf */
    uint16        nDelete;        /* number of tuples to delete */

    spgxlogState stateSrc;

    /* offsets of tuples to delete follow */
    OffsetNumber offsets[FLEXIBLE_ARRAY_MEMBER];
} spgxlogVacuumRoot;

#define SizeOfSpgxlogVacuumRoot offsetof(spgxlogVacuumRoot, offsets)

typedef struct spgxlogVacuumRedirect
{
    uint16        nToPlaceholder; /* number of redirects to make placeholders */
    OffsetNumber firstPlaceholder;    /* first placeholder tuple to remove */
    TransactionId newestRedirectXid;    /* newest XID of removed redirects */

    /* offsets of redirect tuples to make placeholders follow */
    OffsetNumber offsets[FLEXIBLE_ARRAY_MEMBER];
} spgxlogVacuumRedirect;

#define SizeOfSpgxlogVacuumRedirect offsetof(spgxlogVacuumRedirect, offsets)

extern void spg_redo(XLogReaderState *record);
extern void spg_desc(StringInfo buf, XLogReaderState *record);
extern const char *spg_identify(uint8 info);
extern void spg_xlog_startup(void);
extern void spg_xlog_cleanup(void);
extern void spg_mask(char *pagedata, BlockNumber blkno);

#endif                            /* SPGXLOG_H */
